﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Odbc;
using System.IO;

namespace UnityCore2.StdLib
{
    public class StdLibVersion
    {
        private StdLibVersion() { }
        public static string Number
        {
            get
            {
                return "2.0.0.0";
            }
        }
    }

    public class FnFileData
    {
        private FnFileData() { }

        public static void BlockWrite(string filename, string[] data)
        {
            using (StreamWriter sw = new StreamWriter(File.Open(filename, FileMode.Create)))
            {
                for (int i = 0; i < data.Length; i++)
                {
                    if (i < data.Length - 1)
                        sw.WriteLine(data[i]);
                    else
                        sw.Write(data[i]);
                }
                sw.Close();
            }
        }
        public static string[] BlockRead(string filename)
        {
            string[] _tmp = new string[0];
            using (StreamReader sr = new StreamReader(File.Open(filename, FileMode.Open)))
            {
                while (!sr.EndOfStream)
                {
                    string buf = sr.ReadLine();
                    Array.Resize(ref _tmp, _tmp.Length + 1);
                    _tmp[_tmp.Length - 1] = buf;
                }
            }
            return _tmp;
        }
        public static void UFFWrite(string filename, string[] descriptors, string[] data)
        {
            using (StreamWriter sw = new StreamWriter(File.Open(filename, FileMode.Create)))
            {
                sw.WriteLine(FnRawData.Tessellate(true, "UNITY_FILE_FORMAT"));

            }
        }
    }
    public class FnRawData
    {
        private FnRawData() { }

        internal static string Tessellate(bool normalOrder, string data)
        {
            //Check if data is valid
            if (data == null) throw new Exception("Null data detected!");
            string res = "";
            //if length is greater than zero
            if (data.Length > 0)
            {
                //Iterate through the data
                for (int i = 0; i < data.Length; i++)
                {
                    //tessellation algorithm
                    int chrBuf = (int)data[i];
                    chrBuf = (chrBuf - 256) * -1;
                    if (normalOrder)
                        res += (char)chrBuf;
                    else
                        res = (char)chrBuf + res;
                }
            }
            //Return result
            return res;
        }
        internal static string[] Tessellate(bool normalOrder, string[] data)
        {
            string[] res = new string[data.Length];
            for (int i = 0; i < data.Length; i++)
            {
                res[i] = Tessellate(normalOrder, data[i]);
            }
            return res;
        }
        public static string PutDelimiter(char delimiter, bool tessellate, params string[] data)
        {
            //Parity Check
            if (data == null) throw new Exception("Null data detected!");
            //Prepare buffer for packed data.
            string res = null;
            for (int i = 0; i < data.Length; i++)
            {
                //Perform tessellation or retain data form.
                if (tessellate)
                    res = res + Tessellate(false, data[i]);
                else
                    res = res + data[i];
                //Encode packed data.
                if (i < data.Length - 1) res = res + delimiter;
            }
            return res;
        }
        public static string[] RemDelimiter(char delimiter, bool tessellate, string data)
        {
            //Parity Check
            if (data.Length == 0) throw new Exception("Invalid data length.");
            //Remove the delimiter.
            string[] res = data.Split(delimiter);
            //Check if tessellate is enabled.
            if (tessellate)
            {
                for (int i = 0; i < res.Length; i++)
                {
                    res[i] = Tessellate(false, res[i]);
                }
            }
            return res;
        }
        public static object[] Tokenize(string codeln)
        {
            object[] tmp = new object[0];
            bool isword = false;
            string buf = "";
            for (int i = 0; i < codeln.Length; i++)
            {
                switch (codeln[i])
                {
                    case ' ':
                        if (!isword)
                        {
                            MemPush(ref tmp, buf);
                            buf = "";
                        }
                        else
                        {
                            buf += codeln[i];
                        }
                        break;
                    case '"':
                        if (!isword)
                        {
                            isword = true;
                        }
                        else
                        {
                            isword = false;
                            MemPush(ref tmp, buf);
                            buf = "";
                        }
                        break;
                    default:
                        buf += codeln[i];
                        break;
                }
            }
            if (buf.Length > 0) MemPush(ref tmp, buf);
            return tmp;
        }
        public static void ParseFtpDir(string ftpDir, ref object att, ref object name, ref object date, ref long sz)
        {
            if (ftpDir.Length == 0) throw new Exception("Null data detected!");
            string[] buf = ftpDir.Split(' ');
            //string Att, Dir;
            int numcnt = 0, offset = 4;
            long szBuf = 0;
            //Parser for Linux FTP and Windows FileZilla FTP Server.
            for (int i = 0; i < buf.Length; i++)
            {
                //Count the number value markers, first before the ftp markers and second
                //the file size.
                if (long.TryParse(buf[i], out szBuf)) numcnt++;
                if (numcnt == 2)
                {
                    //Get the attribute
                    string cbuf = "", dbuf = "", abuf = "";
                    if (buf[0][0] == 'd') abuf = "dir"; else abuf = "file";
                    //Get the Date
                    if (!buf[i + 3].Contains(':')) offset++;
                    for (int j = i + 1; j < i + offset; j++)
                    {
                        dbuf += buf[j];
                        if (j < buf.Length - 1) dbuf += " ";
                    }
                    //Get the File/Dir name
                    for (int j = i + offset; j < buf.Length; j++)
                    {
                        cbuf += buf[j];
                        if (j < buf.Length - 1) cbuf += " ";
                    }
                    //Output
                    name = cbuf;
                    att = abuf;
                    date = dbuf;
                    sz = szBuf;
                    offset = 0;
                    break;
                }
            }
        }
        public static void MemPush<T>(ref T[] stack, T data)
        {
            //Check if stack has item to pop.
            if (stack == null) throw new Exception("Stack is null.");
            //Resize stack
            Array.Resize(ref stack, stack.Length + 1);
            //push item to stack
            stack[stack.Length - 1] = data;
        }
        public static T MemStackPop<T>(ref T[] stack)
        {
            //Check if stack has item to pop.
            if (stack == null) throw new Exception("Stack is null.");
            if (stack.Length == 0) throw new Exception("Stack has no data.");
            //Perform stack pop
            T data = stack[stack.Length - 1];
            //Resize stack.
            Array.Resize(ref stack, stack.Length - 1);
            return data;
        }
        public static T MemStackPeek<T>(T[] stack)
        {
            //Check if stack has item to peek.
            if (stack == null) throw new Exception("Stack is null.");
            if (stack.Length == 0) throw new Exception("Stack has no data.");
            //Perform stack pop
            return stack[stack.Length - 1];
        }
        public static T MemQueuePop<T>(ref T[] queue)
        {
            //Check if queue has item to pop.
            if (queue == null) throw new Exception("Queue is null.");
            if (queue.Length == 0) throw new Exception("Queue has no data.");
            //Perform queue pop
            T data = queue[0];
            //Resize queue.
            Array.Copy(queue, 1, queue, 0, queue.Length - 1);
            Array.Resize(ref queue, queue.Length - 1);
            return data;
        }
        public static T MemQueuePeek<T>(T[] queue)
        {
            //Check if queue has item to pop.
            if (queue == null) throw new Exception("Queue is null.");
            if (queue.Length == 0) throw new Exception("Queue has no data.");
            return queue[0];
        }
    }
    public class FnDatabase
    {
        private FnDatabase() { }

        public static DataTable GetTable(OdbcDataReader rdr)
        {
            System.Data.DataTable dt = new System.Data.DataTable();
            try
            {
                for (int i = 0; i < rdr.FieldCount; i++)
                {
                    //Get column type in MySQL schema table. (Column 5 is the data type under SchemaTable).
                    //Add column to query buffer.
                    dt.Columns.Add(rdr.GetName(i), rdr.GetFieldType(i));
                }
                //Read records
                while (rdr.Read())
                {
                    //This is the new implementation
                    object[] vals = new object[rdr.FieldCount];
                    rdr.GetValues(vals);
                    dt.Rows.Add(vals);
                }
                dt.AcceptChanges();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                dt.Dispose();
            }
            //Return output
            return dt;
        }
        public static DataTable CreateTable(string tablename, string[] fieldnames, Type[] fieldtypes)
        {
            if (fieldnames.Length != fieldtypes.Length) throw new Exception("Fieldname and Fieldtype count does not match.");
            DataTable dt = new DataTable(tablename);
            for (int i = 0; i < fieldnames.Length; i++)
            {
                dt.Columns.Add(fieldnames[i], fieldtypes[i]);
            }
            return dt;
        }
        public static void AddNew<T>(ref DataTable datatable, params T[] data)
        {
            if (datatable == null) throw new Exception("DataTable is null.");
            if (data.Length > datatable.Columns.Count) throw new Exception("Data length is greater than field length.");
            DataRow dr = datatable.NewRow();
            for (int i = 0; i < data.Length; i++)
            {
                dr[i] = data[i];
            }
            datatable.Rows.Add(dr);
        }
        public static void SetData<T>(ref DataTable datatable, int rownum, string field, T data)
        {
            if (datatable == null) throw new Exception("DataTable is null.");
            if (rownum > datatable.Rows.Count - 1 || rownum < 0) throw new Exception("Record number is out of bounds.");
            try
            {
                datatable.Rows[rownum][field] = data;
            }
            catch (Exception ex) { throw ex; }
        }
        public static object GetData(ref DataTable datatable, int rownum, string field)
        {
            if (datatable == null) throw new Exception("DataTable is null.");
            if (rownum > datatable.Rows.Count - 1 || rownum < 0) throw new Exception("Record number is out of bounds.");
            object res = null;
            try
            {
                res = datatable.Rows[rownum][field];
            }
            catch (Exception ex) { throw ex; }
            return res;
        }
    }
}